package it.i118.jindice;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: sciovante
 * Date: 15/07/13
 * Time: 17.04
 */
public class Index {


    private static File indexFolder;
    private static boolean transaction = false;

    static void setup(File storageDirectory) {
        indexFolder = new File(storageDirectory, "index");
        if (!indexFolder.exists()) {
            if (!indexFolder.mkdirs()) {
                throw new RuntimeException("Impossibile creare il folder per gli indici:" + indexFolder);
            }
        }
    }

    private static ArrayList<Index> indexList = new ArrayList<Index>();

    public static void openTransaction() {
        transaction = true;
    }

    public static void commit() {
        for (Index index : indexList) {
            index.save();
        }
        transaction = false;
    }

    public static void rollback() {
        for (Index index : indexList) {
            index.load();
        }
        transaction = false;
    }

    public static void drop() {
        for (Index index : indexList) {
            index.clear();
        }
    }

    final Field field;
    final String name;
    private HashMap<String, TreeSet<Long>> map = new HashMap<String, TreeSet<Long>>();

    protected Index(Field field) {
        this.field = field;
        this.name = field.getDeclaringClass().getName() + "." + field.getName();
        indexList.add(this);
        load();
    }

    private String getKey(DataObject dataObject) {
        try {
            Object value = field.get(dataObject);
            if (field.getType().equals(String.class)) {
                return (String) value;
            } else {
                throw new RuntimeException("Tipo non supportato:" + field.getType());
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public String[] keys() {
        Set<String> keys = map.keySet();
        return keys.toArray(new String[keys.size()]);
    }

    public Long[] get(String key) {
        Collection<Long> ids = map.get(key);
        return (ids == null) ? new Long[0] : ids.toArray(new Long[ids.size()]);
    }

    public void clear() {
        map.clear();
        save();
    }

    public void add(DataObject dataObject) {
        String key = getKey(dataObject);
        TreeSet<Long> ids = map.get(key);
        if (ids == null) {
            ids = new TreeSet<Long>();
            map.put(key, ids);
        }
        ids.add(dataObject.get_id());
        save();
    }

    public void addAll(DataObject[] lov) {

        for (DataObject dataObject : lov) {
            String key = getKey(dataObject);
            TreeSet<Long> ids = map.get(key);
            if (ids == null) {
                ids = new TreeSet<Long>();
                map.put(key, ids);
            }
            ids.add(dataObject.get_id());
        }

        save();
    }

    public void remove(Long pk) {
        for (Collection<Long> ids : map.values()) {
            if (ids.remove(pk)) {
                break;
            }
        }
        save();
    }

    private void load() {
        map.clear();
        File file = new File(indexFolder, name + ".json");

        if (file.exists()) {
            JsonReader reader = null;
            try {
                reader = new JsonReader(new FileReader(file));
                reader.beginArray();
                while (reader.hasNext()) {
                    reader.beginObject();
                    String key = reader.nextName();
                    TreeSet<Long> lov = new TreeSet<Long>();

                    reader.beginArray();
                    while (reader.hasNext()) {
                        lov.add(reader.nextLong());
                    }
                    reader.endArray();

                    map.put(key, lov);
                    reader.endObject();
                }
                reader.endArray();

            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException ignore) {
                    }
                }
            }
        }
    }

    private void save() {
        if (!transaction) {
            File file = new File(indexFolder, name + ".json");
            JsonWriter writer = null;
            try {
                writer = new JsonWriter(new FileWriter(file));
                Set<String> keySet = map.keySet();
                writer.beginArray();
                for (String key : keySet) {
                    TreeSet<Long> lov = map.get(key);
                    writer.beginObject();
                    writer.name(key);

                    writer.beginArray();
                    for (Long value : lov) {
                        writer.value(value);
                    }
                    writer.endArray();

                    writer.endObject();
                }

                writer.endArray();
                writer.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                if (writer != null) {
                    try {
                        writer.close();
                    } catch (IOException ignore) {
                    }
                }

            }
        }
    }


}
